/**
 * Created with IntelliJ IDEA.
 * User: zzl
 * Date: 12-12-31
 * To change this template use File | Settings | File Templates.
 */
(function ($, undefined) {

    var DRAG_START_DISTANCE_VALVE = 3;

    var levelIndent = 24;

    var DnD = function (table) {
        var me = this;
        me.$table = $(table);
        me.setup();
    };

    var proto = { constructor: DnD };
    DnD.prototype = proto;

    proto.setup = function () {
        var me = this;

        me.$table.on('mousedown.dnd', function (e) {
            me.table_mousedown(e);
        });
    };

    proto.table_mousedown = function (e) {
        var me = this;

        me.reset();

        if (!$(e.target).is('.label.editable') || e.which != 1) {
            return;
        }

        console.log('@@@@@@@@@@@@ table_mousedown @@@')
        me.$downEl = $(e.target);
        me.downPos = {
            x: e.pageX,
            y: e.pageY
        };

        $(document).on('mousemove.dnd', function (e) {
            me.document_mousemove(e);
        });
        $(document).on('mouseup.dnd', function (e) {
            me.document_mouseup(e);
        });
        $(document).on('selectstart.dnd', function (e) {
            e.preventDefault();
        });
        e.preventDefault();
    };

    proto.reset = function () {
        var me = this;
        me.dragging = false;
        me.$downEl = null;
    };

    proto.document_mousemove = function (e) {
        var me = this;
        if (e.which != 1 || !me.$downEl) {
            return;
        }
        me.dragPos = {
            x: e.pageX,
            y: e.pageY
        };
        me.windowScrollTopBeforeScroll = $(window).scrollTop();
        if (!me.dragging) {
            if (Math.abs(me.dragPos.x - me.downPos.x) >= DRAG_START_DISTANCE_VALVE ||
                Math.abs(me.dragPos.y - me.downPos.y) >= DRAG_START_DISTANCE_VALVE) {
                me.beginDrag();
            }
            else {
                return;
            }
        }
        //dragging
        me.updateDragImagePos(me.dragPos.x, me.dragPos.y);

    };

    proto.updateDragImagePos = function(x, y) {
        var me = this;
        me.$dragImage.css({
            left: (x + 16) + 'px',
            top: (y + 16) + 'px'
        });
        me.$dragOverlay.css({
            left: (x + 2) + 'px',
            top: (y + 16) + 'px'
        });
    };

    proto.beginDrag = function () {
        var me = this;
        me.dragging = true;

        me.$downEl.data('disableEdit', true);
        me.$downNode = me.$downEl.parents('tr:first');
        var downNode = me.$downNode[0];
        me.draggingNodes = todolist.getSelectedSeparatedBranchNodes();

        var selectedNodes = todolist.getSelectedRows();
        me.oriSelectedNodes = selectedNodes;
        var downInSelectedNodes = jsex.inArray(downNode, selectedNodes);
        if (!downInSelectedNodes) {
            todolist.setActiveRow(downNode);
            me.oriSelectedNodes = [downNode];
            me.draggingNodes = [downNode];
        }

        var nodes = [];
        for (var n = 0; n < me.draggingNodes.length; n++) {
            var node = me.draggingNodes[n];
            nodes.push(node);
            todolist.collectDescendants(node, nodes);
        }
        me.draggingNodesAndDescendants = nodes;

        //
        me.$dropIndicator = $('<div class="dropIndicator" style="display:none;">' +
            '<div class="line1"></div><div class="line2"></div>' +
            '<div class="line3"></div><div class="line4"></div>' +
            '</div>');
        me.$dropIndicator.appendTo(document.body);

        //
        todolist.setActiveRow(me.$downNode[0], true);//downInSelectedNodes);

        me.$dragImage = me.$downEl.clone();
        me.$dragImage.addClass('dragImage').css({
            backgroundColor: me.$downEl.css('backgroundColor'),
            fontFamily: me.$downEl.css('fontFamily'),
            fontSize: me.$downEl.css('fontSize'),
            padding: me.$downEl.css('padding'),
            color: jsex.replace(me.$downEl.css('color'), '-1', '255')
        }).appendTo(document.body);

        me.$dragOverlay = $('<div class="dragOverlay"></div>');
        me.$dragOverlay.appendTo(document.body);

        me.$table.find('tbody.nodes').on('mousemove.dnd', 'tr', function (e) {
            me.node_mousemove(e, this);
        });
        me.$table.on('keydown.dnd', function (e) {
            me.table_keydown(e);
        });

        //
        if (ie8) {
            me.$downEl[0].setCapture(true);
            me.$downEl.on('losecapture.dnd', function(e) {
                $(document).off('.dnd');
                me.endDrag();
            });
        }
        else if(false) {
            $(window).on('blur.dnd', function () {
                $(document).off('.dnd');
                me.endDrag();
                $(window).off('blur.dnd');
            });
        }

    };

    proto.checkScroll = function (tr) {
        var me = this;
        var $node = $(tr);

        var pos = $node.offset();
        var height = $node.height();

        var $window = $(window);
        var viewportY1 = $window.scrollTop();
        var viewportH = $window.height();
        var viewportY2 = viewportH + viewportY1;

        var $toShowNode;
        var $nextNode = $node.nextAll(':visible:first');
        var windowScrollTop;
        if ($nextNode.length) {
            var nextNodeBottom = pos.top + height + $nextNode.height();
            if (nextNodeBottom > viewportY2 - 3) {
                windowScrollTop = nextNodeBottom - viewportH + 3;
                $toShowNode = $nextNode;
                pos.top += height;
            }
        }
        var $prevNode;
        if (!windowScrollTop && ($prevNode = $node.prevAll(':visible:first')).length) {
            var prevNodeTop = $prevNode.offset().top;
            if (prevNodeTop < viewportY1 + 3) {
                windowScrollTop = prevNodeTop - 3;
                $toShowNode = $prevNode;
            }
        }
        if (windowScrollTop) {
//            console.log('@@@@@@@@ update window.scrollTop to ' + windowScrollTop);
            $window.scrollTop(windowScrollTop);

            var width = $node.width();
            var level = $node.data('level');
            var levelPad = levelIndent * level + 24 + 8;
            pos.left += levelPad;
            width -= levelPad;

            me.$dropIndicator.css({
                left: pos.left + 'px',
                top: (pos.top - 1) + 'px',
                width: width + 'px'
            }).show();

            me.updateDragImagePos(me.dragPos.x,
                me.dragPos.y + windowScrollTop - me.windowScrollTopBeforeScroll);

            me.scrollTimer = setTimeout(function () {
                me.checkScroll($toShowNode[0]);
            }, 100);

        }
    };

    proto.node_mousemove = function (e, tr) {
        var me = this;

//        console.log('DDDD@' + e.target.nodeName);

        clearTimeout(me.scrollTimer);

        if (me.dropNode && me.dropType === 'child') {
            $(me.dropNode).removeClass('dropParent')
        }
        me.dropNode = tr;
        me.dropType = undefined;

        var $node = $(tr);

        var $target = $(e.target);
        if ($target.hasClass('node-button') && $node.hasClass('collapsed')) {
            clearTimeout(me.expandTimer);
            me.expandTimer = setTimeout(function () {
                todolist.expandNode($node);
            }, 300);
            me.toExpandButton = e.target;
        }
        else {
            if (me.expandTimer) {
                clearTimeout(me.expandTimer);
                me.expandTimer = undefined;
            }
        }

        var pos = $node.offset();
        var height = $node.height();
        var asChild;

        me.checkScroll(tr);

        //
        me.$dragOverlay.removeClass('notAllowed move copy');
        if (!e.ctrlKey && jsex.inArray($node[0], me.draggingNodesAndDescendants)) {
            me.$dropIndicator.hide();
            me.$dragOverlay.addClass('notAllowed');
            return;
        }
        me.$dragOverlay.addClass(e.ctrlKey ? 'copy' : 'move');

        //
        if($node.hasClass('root')) {
            asChild = true;
        }
        else if (e.pageY > pos.top + height * 2 / 3) {
            pos.top += height;
            me.dropType = 'below';
        }
        else if (e.pageY > pos.top + height / 3) {
            asChild = true;
        }
        else {
            me.dropType = 'above';
        }

        //
        if (asChild) {
            me.$dropIndicator.hide();
            $node.addClass('dropParent');
            me.dropType = 'child';
            return;
        }

        var width = $node.width();
        var level = $node.data('level');
        var levelPad = levelIndent * level + 24 + 8;
        pos.left += levelPad;
        width -= levelPad;

        me.$dropIndicator.css({
            left: pos.left + 'px',
            top: (pos.top - 1) + 'px',
            width: width + 'px'
        }).show();

    };

    function isNodeInBranch(branchNode, testNode) {
        if (testNode === branchNode) {
            return true;
        }
        var children = $(branchNode).data('children') || [];
        for (var n = 0; n < children.length; n++) {
            if (isNodeInBranch(children[n], testNode)) {
                return true;
            }
        }
        return false;
    }

    proto.isDescendantOfDownNode = function ($node) {
        var me = this;
        if ($node.data('level') <= me.$downNode.data('level')) {
            return false;
        }
        return isNodeInBranch(me.$downNode[0], $node[0]);
    };

    proto.isDragging = function() {
        var me = this;
        return me.dragging;
    };

    proto.document_mouseup = function (e) {
        var me = this;
        $(document).off('.dnd');
        if (!me.dragging) {
            me.reset();
            return;
        }
        domex.clearSelection();

        console.log('@@@@@@@@@@@@@@ doc_mouseup && dropping.., scrollTop =' + $(window).scrollTop());
        var viewport = domex.getViewport();
        var invalidDrag;
        if (e.pageX < viewport.x1 || e.pageX > viewport.x2 ||
            e.pageY < viewport.y1 || e.pageY > viewport.y2) {
            invalidDrag = true;
        }

        $(me.dropNode).removeClass('dropParent');
        if (me.dropType && !invalidDrag) {

            var below = false, asChild = false;
            if(me.dropType == 'above') {
                below = false;//host.isTopDownMode();
            }
            else if(me.dropType == 'below') {
                below = true;//!host.isTopDownMode();
            }
            else if(me.dropType == 'child') {
                asChild = true;
                below = host.isTopDownMode();
            }

            if (e.ctrlKey) {
                var action = undoman.newAction({
                    action: 'new'
                });
                todolist.suppressMouseSelection();
                var clipboard = new Clipboard();
                clipboard.setTable(me.$table);
                clipboard.copy(me.draggingNodes);
                var pastedNodes = clipboard.paste(me.dropNode, below, asChild);
                console.log('@@@@@@@@@ ' + pastedNodes.length);
                action.nodes = pastedNodes;
                todolist.select(pastedNodes);
//                todolist.setActiveRow(nodes[0], true);
                undoman.onAction(action).afterAction();
            }
            else {
                var action = undoman.newAction({
                    action: 'tree',
                    nodes: me.draggingNodes
                });
                var changedNodes = todolist.changeNodesRelation(
                    me.draggingNodes, me.dropNode, below, asChild);
                action.nodes = changedNodes;
                todolist.select(me.oriSelectedNodes);
                todolist.setActiveRow(me.$downNode[0], true);
                undoman.onAction(action).afterAction();
            }
            todolist.notifyRowVisibilityChanged();

            host.NotifyChange();
        }
//        console.log('@@@@@@@@@@ document_mouseup @@@@@@@');
        me.endDrag();
        todolist.suppressMouseSelection();
        e.preventDefault();

    };

    proto.table_keydown = function (e) {
        var me = this;
        var key = e.which;
        if (key == 27) {
            me.endDrag();
        }
    };

    proto.endDrag = function () {
        var me = this;
        if(!me.dragging) {
            return;
        }
        me.$table.find('tbody.nodes').off('mousemove.dnd');
        me.$dragImage.remove();
        me.$dragOverlay.remove();
        me.$dropIndicator.remove();
        me.$downEl.removeData('disableEdit');
        if (ie8) {
            me.$downEl.off('losecapture.dnd');
            me.$downEl[0].releaseCapture();
        }
        me.$downEl = null;
        if (me.dropNode && me.dropType === 'child') {
            $(me.dropNode).removeClass('dropParent');
        }
        me.dropNode = null;
        me.dropType = undefined;
        me.dragging = false;
        me.draggingNodes = [];
        me.oriSelectedNodes = [];
        domex.clearSelection();
    };

    $.fn.dnd = $.addPluginFn('dnd', DnD);

})(jQuery);